package xdb;

import mysql.MySqlMgr;

import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * xdb是一个简单的文件数据库，支持表操作和事务，不具备表之间的关联关系。
 */
public final class Xdb implements XdbMBean {
    private static boolean isDBServer = true;
    private static final Xdb xdbinstance = new Xdb();
    private volatile XdbConf conf;
    private volatile Tables tables;
    private volatile Angel angel;
    private volatile Checkpoint checkpoint;
    private volatile CheckpointCache checkpointcache;
    private final Set<Integer> allLocalIds = new HashSet<>();
    private volatile boolean isOpen = false;
    private volatile boolean isJson = false;
    private final Random random = new Random(System.currentTimeMillis());
    private final xdb.util.MBeans.Manager xdbmbeans = new xdb.util.MBeans.Manager();
    private volatile StopHandle stopHandle = null;

    private Xdb() {
        Runtime.getRuntime().addShutdownHook(new Thread("xdb.ShutdownHook") {
            @Override
            public void run() {
                Trace.warn("xdb stop from ShutdownHook.");
                Xdb.this.stop();
            }
        });
    }

    public static xdb.util.MBeans.Manager mbeans() {
        return xdbinstance.xdbmbeans;
    }

    public static Xdb getInstance() {
        return xdbinstance;
    }

    public static Random random() {
        return xdbinstance.random;
    }

    public static Executor executor() {
        return Executor.getInstance();
    }

    public static boolean isOpen() {
        return xdbinstance.isOpen;
    }

    public static boolean isDBServer() {
        return isDBServer;
    }

    public static void setDBServer(boolean dbServer) {
        isDBServer = dbServer;
    }

    // package private
    final Angel getAngel() {
        return angel;
    }

    public final Tables getTables() {
        return tables;
    }

    public final Executor getExecutor() {
        return Executor.getInstance();
    }

    public final XdbConf getConf() {
        return conf;
    }

    public final void setConf(XdbConf conf) {
        if (null == conf) {
            throw new NullPointerException();
        }
        this.conf = conf;
        final Executor e = Executor.getInstance(); // volatile
        if (null != e) {
            e.setCorePoolSize(conf.getCorePoolSize(), conf.getProcPoolSize(), conf.getSchedPoolSize());
        }
        XBean._set_xdb_verify_(conf.isXdbVerify());
    }

    /**
     * @deprecated see startWithNetwork
     */
    @Deprecated
    public final boolean start() {
        return start(0);
    }

    /**
     * @deprecated see startWithNetwork
     */
    @Deprecated
    public final synchronized boolean start(int options) {
        if (xdb.util.Dbx.getManager().isOpen()) {
            throw new IllegalAccessError("i hate dbx.");
        }

        if (isOpen) {
            return false;
        }

        if (isDBServer()) {
            //检查数据库表结构是否匹配。added by scm
            if (!xdb.util.DatabaseMetaData.getInstance().isValid()) {
                throw new RuntimeException("Compare metadata failed!");
            }
            xdb.util.DatabaseMetaData.getInstance().saveToDB();
        }

        Trace.warn("xdb start begin");
        {
            // 确保创建好静态实例，避免在以后多线程同时装载时的线程问题。
            // 当然，如果ClassLoader确保装载一个类是不并发的，那么就不需要这样处理。有待确认。
            Lockeys.getInstance();
            new XBean(null, null);
        }

        try {
            Class<?> cls = Class.forName("xtable._Tables_");
            Object instance = cls.newInstance();
            if (!(instance instanceof Tables)) {
                throw new XError("invalid xtable.Tables");
            }

            isJson = conf.isJson();
            if (isDBServer()) {
                Executor.start(conf.getProcedureConf().getMaxExecutionTime(), conf.getCorePoolSize(), conf.getProcPoolSize(), conf.getSchedPoolSize(),
                        conf.getTimeoutPeriod());
                if (!closeCache()) {
                    checkpointcache = new CheckpointCache();
                }
                tables = (Tables) instance;
                tables.open(conf);

                checkpoint = new Checkpoint(tables);
                angel = new Angel();
            } else {
                Executor.start(conf.getProcedureConf().getMaxExecutionTime(), conf.getCorePoolSize(), 2, conf.getSchedPoolSize(),
                        conf.getTimeoutPeriod());
                tables = (Tables) instance;
            }

            isOpen = true; // 到这里,Xdb已经启动完成。而且网络会依赖数据库，先设置标志。

            mbeans().register(this, "xdb:type=Xdb");

            if (isDBServer()) {
                if (!closeCache()) {
                    checkpoint.start();
                    checkpointcache.start();
                }
                angel.start();
            }

            Trace.warn("xdb start end");
            return true;

        } catch (XError e) {
            Trace.error("start", e);
            close();
            throw e;
        } catch (Throwable e) {
            Trace.error("start", e);
            close();
            throw new XError(e);
        }
    }

    private final synchronized void close() {
        isOpen = false;
        Trace.warn("xdb stop begin");
        mbeans().unregisterAll();

        Executor.stop();

        if (null != angel) {
            angel.shutdown();
            angel = null;
        }

        if (null != checkpoint) {
            checkpoint.shutdown();
            checkpoint = null;
        }

        if (null != checkpointcache) {
            checkpointcache.shutdown();
            checkpointcache = null;
        }

        if (null != tables) {
            tables.close();
            tables = null;
        }
        MySqlMgr.GetInstance().Exit();
        Trace.warn("xdb stop end");
    }

    public final void stop() {
        // try { Thread.sleep(60000); } catch (Exception ex) { } // delay stop. debug
        synchronized (this) {
            if (false == isOpen) {
                return;
            }
            isOpen = false; // 马上设置标志
        }
        final StopHandle handle = this.stopHandle;
        if (null != handle) {
            handle.beforeStop();
        }
        this.close();
        if (null != handle) {
            handle.afterStop();
        }
    }

    public void setStopHandle(StopHandle stopHandle) {
        this.stopHandle = stopHandle;
    }

    ///////////////////////////////////////////////////////////////////////////////
    // mbean implement
    @Override
    public void shutdown(String iamsure) {
        if (iamsure.equals("iamsure")) {
            this.stop();
            Trace.warn("halt program from jmx");
            Runtime.getRuntime().halt(0);
        }
    }

    @Override
    public int getAngleInterruptCount() {
        return this.angel.getInterruptCount();
    }

    @Override
    public long getTransactionCount() {
        return Transaction.getTotalCount();
    }

    @Override
    public long getTransactionFalse() {
        return Transaction.getTotalFalse();
    }

    @Override
    public long getTransactionException() {
        return Transaction.getTotalException();
    }

    @Override
    public void testAlive(long timeout) throws InterruptedException, ExecutionException, TimeoutException {
        this.getExecutor().testAlive(timeout);
        // more test ...
    }

    public CheckpointMBean getCheckpointMBean() {
        return checkpoint;
    }

    @Override
    public Map<String, AtomicInteger> top(String nsClass, String nsLock) {
        return new xdb.util.StackTrace(nsClass, nsLock).top();
    }

    public void initLocalIds(Collection<Integer> localIds) {
        allLocalIds.addAll(localIds);
    }

    /**
     * @param roleId
     * @return
     * @author yangzhenyu
     */
    public boolean validateInsertRoleRecord(Long roleId) {
        for (int localId : allLocalIds) {
            long check = roleId / 4096;
            if (roleId - check * 4096 == localId) {
                return true;
            }
        }

        return false;
    }

    public CheckpointCache getCheckpointcache() {
        return checkpointcache;
    }

    public boolean isJson() {
        return isJson;
    }

    public void setJson(boolean isJson) {
        this.isJson = isJson;
    }

    /**
     * xdb是否关闭缓存
     *
     * @return
     */
    public boolean closeCache() {
        return conf.closeCache();
    }

    public interface StopHandle {
        /**
         * XDB 停止前调用。
         * 此时XDB数据等功能都能使用。但由于isOpen已经为false，依赖于这个标志的功能会受影响。
         */
        void beforeStop();

        /**
         * XDB 停止后调用。
         */
        void afterStop();
    }

}